home *** CD-ROM | disk | FTP | other *** search
/ MacTech 1 to 12 / MacTech-vol-1-12.toast / Source / MacTech® Magazine / Volume 07 - 1991 / 07.03 Mar 91 / Cursor Test Source / CursorCtrl.c < prev   
Encoding:
C/C++ Source or Header  |  1990-12-19  |  12.6 KB  |  428 lines  |  [TEXT/KAHL]

  1. /**********************************************************
  2. Copyright (c) 1990 Richard Lesh, All Rights Reserved 
  3. **********************************************************/
  4.  
  5. #include <MacHeaders>
  6. #include <Color.h>
  7. #include <VRetraceMgr.h>
  8.  
  9. /**********************************************************
  10. Module: Cursor Control
  11.  
  12. This module provides support for the use of dynamic cursors
  13. which give better feedback to the user that the program is
  14. working properly.  This module has an interface similar to
  15. the MPW version CursorCtrl.c but does not necessarily
  16. function in an identical manner.  (MPW does not support
  17. color cursors).  This version uses a VBL task to smooth out
  18. the cursor animation.  It does not, hoever, allow the cursor
  19. to continue spinning in the event of a system crash.  The
  20. application must execute SpinCursor on a regular basis to
  21. keep the cursor spinning.  RotateCursor is not supported in
  22. this version.  InitCursorCtrl is called to initialize the
  23. cursors or to change them to a new set of cursors.
  24. StopCursor is called to halt the cursor animation so that
  25. it can then be set to the arrow or other application defined
  26. cursors.
  27.  
  28. Routines:
  29. InitCursorCtrl() -- Initializes the module & loads cursors.
  30. QuitCursorCtrl() -- Cleans up when animated cursors are no
  31.                     longer needed.
  32. SpinCursor()     -- Starts or continues cursor animation.
  33. StopCursor()     -- Stops the cursor animation.
  34. SetCursorSpeed() -- Sets the speed of cursor rotation.
  35. **********************************************************/
  36.  
  37. /**********************************************************
  38. Definitions
  39. **********************************************************/
  40. #ifndef NULL
  41. #define NULL 0L
  42. #endif
  43.  
  44. /**********************************************************
  45. TypeDefs and Enums
  46. **********************************************************/
  47. /* Animated cursor control structure 'acur' */
  48. typedef struct {
  49.     short n;
  50.     short index;
  51.     union {
  52.         Handle cursorHdl;
  53.         short resID;
  54.     }frame[1];
  55. }acurRec,*acurPtr,**acurHandle;
  56.  
  57. /**********************************************************
  58. Private Globals
  59. **********************************************************/
  60. /* Current 'acur' resource handle */
  61. static acurHandle gCurrentHdl=NULL;
  62. /* True if using color cursors */
  63. static Boolean gColorCursor=FALSE;
  64. /* VBL task record for the cursor */
  65. static VBLTask *gCursorTask=NULL;
  66. /* Number of cycles to spin cursor */
  67. static short gSpinCycles=0;
  68. /* Number of ticks between changes */
  69. static short gSpeed=10;
  70. /* TRUE after SpinCursor() and FALSE after StopCursor() */
  71. static Boolean isCursorRunning=FALSE;
  72.  
  73. /**********************************************************
  74. Prototypes
  75. **********************************************************/
  76. /* Functions to be exported */
  77. void InitCursorCtrl(acurHandle h);
  78. void QuitCursorCtrl(void);
  79. void SpinCursor(short cycles);
  80. void StopCursor(void);
  81. void SetCursorSpeed(short newSpeed);
  82.  
  83. /* Functions to be used internally */
  84. void DisposCursors(void);
  85. Boolean GetMonoCursors(acurHandle h);
  86. Boolean GetColorCursors(acurHandle h);
  87. pascal void SpinCursorTask(void);
  88.  
  89. Boolean hasColorQD(void);
  90. VBLTask *InstallVBLTask(ProcPtr proc,short ticks);
  91. void RemoveVBLTask(VBLTask *taskPtr);
  92.  
  93. /**********************************************************
  94. Routine:    InitCursorCtrl(acurHandle resHdl)
  95.  
  96. This routine initializes the CursorCtrl module using the
  97. 'acur' resource indicated via the argument resHdl.  If
  98. resHdl is NULL then the 'acur' resource with ID=128 is
  99. loaded. If the machine has ColorQD, InitCursorCtrl first
  100. attempts to load the color cursor 'crsr' resources with the
  101. IDs indicated in the 'acur' resource.  If this fails or if
  102. the machine does not have ColorQD, InitCursorCtrl attempts
  103. to load the normal cursor 'CURS' resources with the IDs
  104. indicated in the 'acur' resource.  If this action fails,
  105. all subsequent calls to SpinCursor will simply set the
  106. cursor to the watch cursor.
  107.  
  108. InitCursorCtrl should be called as follows:
  109.  
  110. InitCursorCtrl((acurHandle)GetResource('acur',200));
  111.  
  112. If GetResource is unable to find the 'acur' resource it
  113. returns NULL which can be handled by InitCursorCtrl.
  114. InitCursorCtrl will mark the resource as unpurgable and
  115. will be responsible for releasing the all cursor storage
  116. and the 'acur' resource when called again with a new 'acur'
  117. handle.  If the new handle is the same as the current
  118. handle, nothing will be done.  Since 'crsr' resources are
  119. just templates for a color cursor, you should make sure
  120. that all 'crsr' resources are marked purgable in the
  121. resource file since GetCCursor does not release these
  122. resources.
  123. **********************************************************/
  124.  
  125. void InitCursorCtrl(acurHandle h)
  126. {
  127.     short i,j;
  128.     Boolean useColorCursors;
  129.  
  130.     useColorCursors=hasColorQD();
  131.     
  132.     if (!h) h=(void *)GetResource('acur', 128);
  133.     if (h && h!=gCurrentHdl){
  134.         HNoPurge(h);
  135.         MoveHHi(h);
  136.         HLock(h);
  137.         
  138. /**********************************************************
  139. Get new cursors.
  140. **********************************************************/
  141.         StopCursor();
  142.         if (useColorCursors)
  143.             useColorCursors=GetColorCursors(h);
  144.         if (!useColorCursors && !GetMonoCursors(h)) return;
  145.  
  146.         DisposCursors();
  147.         gCurrentHdl=h;
  148.         gColorCursor=useColorCursors;
  149.         (*h)->index=0;
  150.     }
  151. }
  152.  
  153. /**********************************************************
  154. Routine:    QuitCursorCtrl()
  155.  
  156. Shuts down the cursor control module.
  157. **********************************************************/
  158.  
  159. void QuitCursorCtrl()
  160. {
  161.     DisposCursors();
  162.     if (gCursorTask) RemoveVBLTask(gCursorTask);
  163.     gCursorTask=NULL;
  164. }
  165.  
  166. /**********************************************************
  167. Routine:    DisposCursors()
  168.  
  169. Disposes the cursors pointed to in the 'acur' structure.
  170. **********************************************************/
  171.  
  172. void DisposCursors()
  173. {
  174.     register short i,j;
  175.     
  176.     StopCursor();
  177.     if (gCurrentHdl){
  178.         j=(*gCurrentHdl)->n;
  179.         if (gColorCursor)
  180.             for (i=0;i<j;i++)
  181.                 DisposCCursor((*gCurrentHdl)->frame[i].cursorHdl);
  182.         else
  183.             for (i=0;i<j;i++)
  184.                 DisposHandle((*gCurrentHdl)->frame[i].cursorHdl);
  185.         ReleaseResource(gCurrentHdl);
  186.         gCurrentHdl=NULL;
  187.     }
  188. }
  189.  
  190. /**********************************************************
  191. Routine:    GetMonoCursors(acurHandle h)
  192.  
  193. This is an internal routine that loads the normal cursors
  194. ('CURS') from the resource file. In the 'acur' resource,
  195. the resource ID of the cursor is stored in the frame union.
  196. When the cursor has been loaded, its handle is then stored
  197. in the frame union.  The function returns TRUE if it was
  198. successful at loading in all the cursors and FALSE if there
  199. was an error.
  200. **********************************************************/
  201.  
  202. static Boolean GetMonoCursors(acurHandle h)
  203. {
  204.     short i,j;
  205.     CursHandle cursHdl;
  206.  
  207.     if (h){
  208.         j=(*h)->n;
  209.         for (i=0;i<j;i++){
  210.             cursHdl=GetCursor((*h)->frame[i].resID);
  211.             if (cursHdl==NULL){
  212.                 for (j=0;j<i;j++)
  213.                     DisposHandle((*h)->frame[j].cursorHdl);
  214.                 return(FALSE);
  215.             }else{
  216.                 DetachResource(cursHdl);
  217.                 (*h)->frame[i].cursorHdl=(Handle)cursHdl;
  218.             }
  219.         }
  220.     }
  221.     return(TRUE);
  222. }
  223.  
  224. /**********************************************************
  225. Routine:    GetColorCursors(acurHandle h)
  226.  
  227. This is an internal routine that loads the color cursors
  228. ('crsr') from the resource file. In the 'acur' resource,
  229. the resource ID of the cursor is stored in the frame union.
  230. When the cursor has been loaded, its handle is then stored
  231. in the frame union.  The function returns TRUE if it was
  232. successful at loading in all the cursors and FALSE if there
  233. was an error.  The 'crsr' resources should be set purgable.
  234. **********************************************************/
  235.  
  236. static Boolean GetColorCursors(acurHandle h)
  237. {
  238.     short i,j;
  239.     CCrsrHandle cursHdl;
  240.     Boolean result=TRUE;
  241.  
  242.     if (h){
  243.         j=(*h)->n;
  244.         HideCursor();
  245.         for (i=0;i<j;i++){
  246.             cursHdl=GetCCursor((*h)->frame[i].resID);
  247.             if (cursHdl==NULL){
  248.                 for (j=0;j<i;j++)
  249.                     DisposCCursor((*h)->frame[j].cursorHdl);
  250.                 result=FALSE;
  251.                 break;
  252.             }else{
  253.                 (*h)->frame[i].cursorHdl=(Handle)cursHdl;
  254.                 SetCCursor((*h)->frame[i].cursorHdl);
  255.             }
  256.         }
  257.         InitCursor();
  258.     }
  259.     return(result);
  260. }
  261.  
  262. /**********************************************************
  263. Routine: SpinCursor(short seconds)
  264.  
  265. This routine sets gSpinCycles to seconds*60/gSpeed which is
  266. the number of times that the cursor should spin before
  267. stopping. The seconds parameter should therefore be set to
  268. just slightly longer than the time estimated to execute the
  269. code up to the next SpinCursor call.  When the gSpinCycles
  270. value counts down to zero the cursor will no longer spin
  271. and the user will thereby be notified that something is
  272. awry. If there is no current 'acur' resource, this routine
  273. will set the cursor the the watch cursor.
  274. **********************************************************/
  275.  
  276. void SpinCursor(short seconds)
  277. {
  278.     static long counter=0;
  279.     
  280.     if (gCurrentHdl==0) InitCursorCtrl(NULL);
  281.     if (gCurrentHdl){
  282.             if (!gCursorTask)
  283.                 gCursorTask=InstallVBLTask((ProcPtr)SpinCursorTask,
  284.                     gSpeed);
  285.             if (gCursorTask){
  286.                 if (gSpinCycles==0){
  287.                     if (gColorCursor) 
  288.                         SetCCursor((*gCurrentHdl)->frame[
  289.                             (*gCurrentHdl)->index].cursorHdl);
  290.                     else 
  291.                         SetCursor(*(*gCurrentHdl)->frame[
  292.                             (*gCurrentHdl)->index].cursorHdl);
  293.                 }
  294.                 gSpinCycles=seconds*60/gSpeed;
  295.             }
  296.     }else
  297.         SetCursor(*GetCursor(watchCursor));
  298.     isCursorRunning=TRUE;
  299. }
  300.  
  301. /**********************************************************
  302. Routine:    SpinCursorTask()
  303.  
  304. This is the VBL task routine.  If the gSpinCycles global is
  305. not zero it will decrement gSpinCycles and then advance the
  306. cursor to the next one specified by the 'acur' resource in
  307. gCurrentHdl.  This routine does call SetCCursor which can
  308. call the memory manager.  The results could be severe if
  309. the cursors had not be preloaded.  Since application VBL
  310. tasks are only called when the application is in the
  311. foreground, the global CurrentA5 used by SetCurrentA5()
  312. will be the correct value.
  313. **********************************************************/
  314.  
  315. static pascal void SpinCursorTask()
  316. {
  317.     long oldA5;
  318.  
  319.     oldA5=SetCurrentA5();
  320.     gCursorTask->vblCount=gSpeed;
  321.     if (gSpinCycles){
  322.         gSpinCycles--;
  323.         (*gCurrentHdl)->index++;
  324.         (*gCurrentHdl)->index%=(*gCurrentHdl)->n;
  325.         if (gColorCursor) 
  326.             SetCCursor((*gCurrentHdl)->frame[(*gCurrentHdl)
  327.                 ->index].cursorHdl);
  328.         else
  329.             SetCursor(*(*gCurrentHdl)->frame[(*gCurrentHdl)
  330.                 ->index].cursorHdl);
  331.     }
  332.     SetA5(oldA5);
  333. }
  334.  
  335. /**********************************************************
  336. Routine:    StopCursor()
  337.  
  338. This routine will stop the cursor animation by setting the
  339. gSpinCycles global to zero.  This must be called before the
  340. application changes the cursor to a normal cursor because
  341. the VBL task will continue to set the cursor to the
  342. animated cursors until the gSpinCycles count has run out.
  343. **********************************************************/
  344.  
  345. void StopCursor()
  346. {
  347.     gSpinCycles=0;
  348.     isCursorRunning=FALSE;
  349.     if (gCursorTask) RemoveVBLTask(gCursorTask);
  350.     gCursorTask=NULL;
  351. }
  352.  
  353. /**********************************************************
  354. Routine:    SetCursorSpeed(short newSpeed)
  355.  
  356. This routine sets the number of ticks that must occur
  357. before the cursor will change to the next one in the
  358. animation sequence.
  359. **********************************************************/
  360.  
  361. void SetCursorSpeed(short newSpeed)
  362. {
  363.     if (newSpeed>0 && newSpeed<=60)
  364.         gSpeed=newSpeed;
  365. }
  366.  
  367. /**********************************************************
  368. Routine:    Boolean hasColorQD()
  369.  
  370. Predicate that returns TRUE if the current machine supports
  371. Color Quickdraw.
  372. **********************************************************/
  373.  
  374. #define SysEnvironsVersion 2
  375. Boolean hasColorQD()
  376. {
  377.     OSErr theErr;
  378.     SysEnvRec theWorld;
  379.     
  380.     theErr=SysEnvirons(SysEnvironsVersion,&theWorld);
  381.  
  382.     if (theErr == 0 && theWorld.hasColorQD) return(TRUE);
  383.     else return(FALSE);
  384. }
  385.  
  386. /**********************************************************
  387. Routine: VBLTask *InstallVBLTask(ProcPtr proc,short ticks)
  388.  
  389. This routine installs the VLBTask pointed to by the ProcPtr
  390. which will begin execution after the number of ticks
  391. specified.  A pointer to a VBL task record is returned.
  392. NULL is returned if the task could not be set up.
  393. **********************************************************/
  394.  
  395. VBLTask *InstallVBLTask(ProcPtr proc,short ticks)
  396. {
  397.     OSErr err;
  398.     VBLTask *taskPtr;
  399.     
  400.     taskPtr=(VBLTask *)NewPtr(sizeof(VBLTask));
  401.     if (taskPtr){
  402.         taskPtr->qType=vType;
  403.         taskPtr->vblAddr=proc;
  404.         taskPtr->vblCount=ticks;
  405.         taskPtr->vblPhase=0;
  406.         err=VInstall((QElemPtr)taskPtr);
  407.         if (err!=noErr){
  408.             DisposPtr(taskPtr);
  409.             taskPtr=NULL;
  410.         }
  411.     }
  412.     return(taskPtr);
  413. }
  414.  
  415. /**********************************************************
  416. Routine: void RemoveVBLTask(VBLTask *taskPtr)
  417.  
  418. Removes the VBL task specified by taskPtr that was
  419. installed using InstallVBLTask.    It also disposes of the
  420. memory set up in that call.
  421. **********************************************************/
  422.  
  423. void RemoveVBLTask(VBLTask *taskPtr)
  424. {
  425.     VRemove((QElemPtr)taskPtr);
  426.     DisposPtr(taskPtr);
  427. }
  428.